import numpy as np
import threading
import csv
import time
import json
import random
from datetime import datetime
import requests
import argparse
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization, hashes

with open("public_key.pem", "rb") as key_file:
    public_key = serialization.load_pem_public_key(key_file.read())

parser = argparse.ArgumentParser(description="manual to this script")
parser.add_argument("--url", type=str, default="http://10.242.160.16:5000/")
parser.add_argument("--id", type=str, default="0")
parser.add_argument("--num", type=int, default=10)
args = parser.parse_args()

# 定义收缩压和舒张压的均值和标准差
sbps = np.array([131, 118, 114, 114, 118, 122, 144, 140, 140, 133, 130, 122, 123, 120, 117, 118, 119, 122, 122, 125, 119, 131, 144, 137])
dbps = np.array([70, 68, 68, 69, 77, 86, 90, 88, 82, 81, 79, 81, 76, 74, 77, 73, 73, 77, 81, 85, 87, 89, 83, 80])
pps = sbps - dbps

num_devices = args.num  # 设备数量
url = args.url
sid = args.id


def read_csv(file_path):
    with open(file_path, "r") as file:
        reader = csv.reader(file)
        data = list(reader)
    return data


tickets = read_csv("tickets.csv")


class Device:
    access_token = ""
    dev_id = -1
    ticket = ""

    def __init__(self, id):
        self.dev_id = id
        self.ticket = tickets[int(sid) * 10 + id][1]
        self.get_token()

    def generate_data(self):
        # 获得当前时间小时数
        now_hour = datetime.now().hour
        now_minute = datetime.now().minute

        # 生成模拟数据
        dbp = np.random.normal(dbps[now_hour], 12)
        pp = np.random.normal(pps[now_hour], 9)
        sbp = dbp + pp
        data = {"id": sid.zfill(5) + str(self.dev_id).zfill(5), "time": time.time(), "h": now_hour, "m": now_minute, "sbp": sbp, "dbp": dbp}
        json_data = json.dumps(data)  # 将字典转换为 JSON 格式字符串
        encrypted_data = self.encrypt_data(json_data)  # 使用公钥加密 JSON 字符串
        encrypted_data_hex = encrypted_data.hex()  # 将加密后的数据转换为十六进制格式（以便通过 JSON 传输）
        # print(data)
        self.push_data(encrypted_data_hex)
        print(now_hour, now_minute, dbp, sbp)

    def encrypt_data(self, data):
        """
        使用 RSA 公钥加密数据。
        :param public_key: 加载的公钥对象
        :param data: 要加密的字符串数据
        :return: 加密后的数据（字节格式）
        """
        return public_key.encrypt(data.encode(), padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))

    def push_data(self, data, retries=5, timeout=20):
        for attempt in range(retries):
            try:
                headers = {"Authorization": f"Bearer {self.access_token}"}
                response = requests.post(url + "data", json={"data": data}, timeout=timeout, headers=headers)
                response.raise_for_status()  # 检查请求是否成功
                return response.json()
            except requests.exceptions.Timeout:
                print(f"Attempt {attempt + 1} timed out. Retrying...")
            except requests.HTTPError as e:
                if response.status_code == 401 and "error" in response.json():
                    print(response.json().get("error"))
                    self.get_token()
                else:
                    print(f"HTTP error: {e}")
                    break
        return None

    def get_token(self):
        try:
            response = requests.post(url + "get_access_token", json={"ticket": self.ticket}, timeout=10)
            response.raise_for_status()  # 检查请求是否成功
            self.access_token = response.json().get("access_token")
            return response.json().get("access_token")
        except requests.exceptions.HTTPError as e:
            print(f"HTTP error: {e}")
        except requests.exceptions.Timeout:
            print("Token request timed out.")
        except Exception as e:
            print(f"An error occurred: {e}")
        return None


def start_devices(id):
    device = Device(id)
    for _ in range(144):
        t = time.time()
        device.generate_data()
        time.sleep(600 - (time.time() - t))


for i in range(num_devices):
    threading.Thread(target=start_devices, args=(i,)).start()
    time.sleep(random.random() * 10)
